#pragma once

#include <memory>
#include <unordered_map>
#include <string>
#include <vector>

#include "core/IDevice.h"
#include "core/gateway_config.h"

class DeviceManager {
 public:
    static DeviceManager& getInstance();

    bool initialize();
    bool initialize(const GatewayConfig& config);

    std::shared_ptr<IDevice> createDevice(const std::string& deviceName);
    std::shared_ptr<IDevice> getOrCreateDevice(const std::string& deviceName);
    bool removeDevice(const std::string& deviceName);

    std::vector<std::string> listAvailableDevices() const;
    std::vector<std::string> listInitializedDevice() const;

    bool autoCreateAllDevices();

    // Fatory method for creating a specific type of device.
    template<typename DeviceType, typename... Args>
    std::shared_ptr<DeviceType> createSpecificDevice(Args&&... args) {
        static_assert(std::is_base_of<IDevice, DeviceType>::value,
                      "Device type must derive from IDevice");
        auto device = std::shared_ptr<DeviceType>(new DeviceType(std::forward<Args>(args)...));
        m_devices[device->getName()] = device;
        return device;
    }

 private:
    DeviceManager() = default;
    ~DeviceManager() = default;

    // deleted copy and assignment constructor
    DeviceManager(const DeviceManager&) = delete;
    DeviceManager& operator=(const DeviceManager&) = delete;

    std::unordered_map<std::string, std::shared_ptr<IDevice>> m_devices;
    std::vector<std::string> m_availableDevices;
};